home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / et / et3_0-a1.lha / et3 / src / TextView.C < prev    next >
C/C++ Source or Header  |  1992-08-26  |  22KB  |  984 lines

  1. #ifdef __GNUG__
  2. #pragma implementation
  3. #endif
  4.     
  5. #include "TextView.h"
  6.  
  7. #include "Class.h"
  8. #include "TextCmd.h"
  9. #include "CmdNo.h"
  10. #include "String.h"
  11. #include "View.h"
  12. #include "Window.h"
  13. #include "Menu.h"
  14. #include "Error.h"
  15. #include "ChangeDialog.h"
  16. #include "Document.h"
  17. #include "RegularExp.h"
  18. #include "ClipBoard.h"
  19. #include "TextFormatter.h"
  20. #include "Env.h"
  21. #include "Data.h"
  22. #include "Math.h"
  23.  
  24. #include "SortedOList.h"
  25. #include "TextItem.h"
  26. #include "MenuBar.h"
  27.  
  28. const int cCaretHt = 4,
  29.       cCaretWd = 7;
  30.  
  31. static u_short CaretBits[]= {
  32. #   include "images/Caret.image"
  33. };
  34. SmartBitmap CaretImage(Point(cCaretWd, cCaretHt), CaretBits);
  35.  
  36. static int InputMap[] = {
  37. 0,       /* 0x00 */
  38. 0,       /* 0x01 */
  39. 0,       /* 0x02 */
  40. 0,       /* 0x03 */
  41. 0,       /* 0x04 */
  42. 0,       /* 0x05 */
  43. 0,       /* 0x06 */
  44. 0,       /* 0x07 */
  45. 0,       /* 0x08 */
  46. '\t',    /* 0x09 */
  47. '\r',    /* 0x0a */
  48. 0,       /* 0x0b */
  49. 0x0c,    /* 0x0c */
  50. '\n',    /* 0x0d */
  51. 0,       /* 0x0e */
  52. 0,       /* 0x0f */
  53. 0,       /* 0x10 */
  54. 0,       /* 0x11 */
  55. 0,       /* 0x12 */
  56. 0,       /* 0x13 */
  57. 0,       /* 0x14 */
  58. 0,       /* 0x15 */
  59. 0,       /* 0x16 */
  60. 0,       /* 0x17 */
  61. 0,       /* 0x18 */
  62. 0,       /* 0x19 */
  63. 0,       /* 0x1a */
  64. 0,       /* 0x1b */
  65. 0,       /* 0x1c */
  66. 0,       /* 0x1d */
  67. 0,       /* 0x1e */
  68. 0,       /* 0x1f */
  69. };
  70.  
  71. static int *inputmap= InputMap;
  72.  
  73. static Ink *caretColor;
  74.  
  75. void SwapSelPoints(SelPoint &p1, SelPoint &p2)
  76. {
  77.     SelPoint tmp;
  78.  
  79.     tmp= p1; p1= p2; p2= tmp;
  80. }
  81.  
  82. //----- TextView Methods -------------------------------------------------------
  83.  
  84. NewMetaImpl(TextView, StaticTextView, (TB(updateSelection), TB(inTextSelector), 
  85.     TB(enabled), TP(stopChars), TP(typing), TP(findChange), TP(scratchText), 
  86.     T(start.ch), T(start.line), T(start.viewp), T(end.ch), T(end.line), 
  87.     T(end.viewp)));
  88.  
  89. TextView::TextView(
  90.     EvtHandler *eh, Rectangle r, Text *t, 
  91.     bool w, TextViewFlags f, Point b, 
  92.     TViewAlign ta, int id
  93. ) : StaticTextView(eh, r, t, w, f, b, ta, id) 
  94. {
  95.     Init();
  96. }
  97.  
  98. void TextView::InitNew()
  99. {
  100.     View::InitNew();
  101.     inTextSelector= FALSE;
  102. }  
  103.  
  104. void TextView::Init()
  105. {
  106.     PrivSetSelection(0, 0, FALSE);
  107.     typing= 0;
  108.     stopChars= 0;
  109.     updateSelection= TRUE;
  110.     text->AddObserver(this);
  111.     scratchText= text->MakeScratchText(0, cMaxBatchedIns);
  112.     findChange= 0;
  113.     active= TRUE;
  114.     inTextSelector= FALSE;
  115.     enabled= FALSE;
  116.     if (Env::GetValue("TextView.DragAndDrop", TRUE))
  117.     SetFlag(eTextViewDragAndDrop);
  118.     SetFlag(eVObjKbdFocus);
  119. }
  120.  
  121. TextView::~TextView()
  122. {
  123.     if (text)
  124.     text->RemoveObserver(this);
  125.     SafeDelete(stopChars);
  126.     if (findChange)
  127.     findChange->Close();
  128.     SafeDelete(findChange);
  129.     SafeDelete(scratchText);
  130. }
  131.  
  132. void TextView::InvalidateSelection()
  133. {
  134.     if (AnySelection())
  135.     InvalidateRange(start.line, start.viewp, end.line, end.viewp);
  136. }
  137.  
  138. void TextView::Draw(Rectangle r)
  139. {
  140.     if (GrHasColor() && CanShowSelection() && AnySelection())
  141.     Invert(start.line, start.viewp, end.line, end.viewp);
  142.     StaticTextView::Draw(r);
  143.     if (CanShowSelection() && AnySelection()) {
  144.     DrawCaret(start.viewp, start.line, On);
  145.     if (!GrHasColor())
  146.         Invert(start.line, start.viewp, end.line, end.viewp);
  147.     }
  148. }
  149.     
  150. void TextView::DrawCaret(Point p, int line, HighlightState)
  151. {
  152.     if (! inTextSelector && AnySelection() && Caret()) {
  153.     int bh= BaseHeight(line);
  154.     p+= GetInnerOrigin();
  155.  
  156.     if (caretColor == 0) {
  157.         if (GrHasColor()) {
  158.         caretColor= new_RGBColor(255, 0, 0);
  159.         //Env::Bind(caretColor, "TextView.CaretColor");
  160.         } else
  161.         caretColor= ePatXor;
  162.     }
  163.     
  164.     if (LineHeight(line) - bh >= cCaretHt) {
  165.         Rectangle r(p.x-cCaretHt, p.y+bh, cCaretWd, cCaretHt);
  166.         GrPaintBitMap(r, CaretImage, caretColor);
  167.     }
  168.     p.x--;
  169.     GrPaintLine(caretColor, 0, eDefaultCap, p, Point(p.x, p.y+bh));
  170.     }
  171. }
  172.  
  173. void TextView::SetKeyMap(int *map)
  174. {
  175.     inputmap= map;
  176. }
  177.  
  178. //---- highlight the characters in the given range -----------------------
  179.  
  180. void TextView::Invert(int from, Point fp, int to, Point tp)
  181. {
  182.     if (AnySelection() && !Caret()) {
  183.     Rectangle r;
  184.     Ink *c= gHighlightColor;
  185.     
  186.     if ((from == to && fp.x > tp.x) || from > to) { // normalize range
  187.         SwapRange(from, to);
  188.         Swap(fp, tp);   
  189.     }
  190.     
  191.     if (from == to) {
  192.         r= Rectangle(LineToPoint(from)+Point (fp.x,0),
  193.                Point(tp.x-fp.x, LineHeight(from)));
  194.         GrPaintRect(r+GetInnerOrigin(), c);
  195.         return;
  196.     }
  197.     r= Rectangle(LineToPoint(from)+ Point(fp.x,0),
  198.               Point(GetInnerExtent().x-fp.x, LineHeight(from)));
  199.     GrPaintRect(r+GetInnerOrigin(), c);
  200.     r= Rectangle(LineToPoint(from + 1), Point(GetInnerExtent().x,
  201.               LineToPoint(to).y - LineToPoint(from + 1).y));
  202.     GrPaintRect(r+GetInnerOrigin(), c);
  203.     r= Rectangle(LineToPoint(to), Point(tp.x, LineHeight(to)));
  204.     GrPaintRect(r+GetInnerOrigin(), c);
  205.     }
  206. }
  207.  
  208. void TextView::RepairAll()
  209. {
  210.     StaticTextView::RepairAll();
  211.     if (CanShowSelection())
  212.     SetSelection(start.ch, end.ch, FALSE);
  213. }
  214.  
  215. void TextView::ClearSelection(bool redraw)
  216. {
  217.     if (AnySelection()) {
  218.     InvalidateSelection();
  219.     start.ch= end.ch= -1;
  220.     if (redraw)
  221.         UpdateEvent();
  222.     }
  223. }
  224.  
  225. void TextView::ShowSelection(bool redraw, bool show)
  226. {
  227.     if (enabled != show) {
  228.     enabled= show;
  229.     InvalidateSelection();
  230.     if (redraw)
  231.         UpdateEvent();
  232.     }
  233. }
  234.  
  235. bool TextView::KbdFocus(bool in)
  236. {
  237.     ShowSelection(FALSE, in);
  238.     return TRUE;
  239. }
  240.  
  241. void TextView::Enable(bool enable, bool redraw)
  242. {
  243.     StaticTextView::Enable(enable, redraw);
  244.     if (! enable)
  245.     HideSelection(redraw);
  246.  
  247. void TextView::PrivSetSelection(SelPoint s, SelPoint e, bool redraw)
  248. {
  249.     InvalidateSelection();          // invalidate old selection
  250.     start= s;
  251.     end= e;
  252.     InvalidateSelection();          // invalidate new selection
  253.     if (redraw)
  254.     UpdateEvent();
  255.     Send(GetId(), cPartSelectionChanged, this);
  256. }
  257.  
  258. void TextView::PrivSetSelection(int s, int e, bool redraw)
  259. {
  260.     SelPoint sp, ep;
  261.  
  262.     if (text == 0)
  263.     return;
  264.     
  265.     int end= text->End();
  266.     sp.ch= Math::Range(0, end, s);
  267.     ep.ch= Math::Range(0, end, e);
  268.     sp.ch= CharToPoint(sp.ch, &sp.line, &sp.viewp);
  269.     if (sp.ch != ep.ch)
  270.     ep.ch= CharToPoint(ep.ch, &ep.line, &ep.viewp);
  271.     else
  272.     ep= sp;
  273.     PrivSetSelection(sp, ep, redraw);
  274. }
  275.  
  276. void TextView::SetSelection(int s, int e, bool redraw)
  277. {
  278.     DoneTyping();
  279.     PrivSetSelection(s, e, FALSE);
  280.     ShowSelection(redraw);
  281. }
  282.  
  283. void TextView::SelectionAsString(byte *buf, int ma)
  284. {
  285.     GetText()->CopyInStr(buf, ma, start.ch, end.ch);
  286. }
  287.  
  288. Text *TextView::SelectionAsText()
  289. {
  290.     return text->Save(start.ch, end.ch);
  291. }
  292.  
  293. void TextView::SetDragAndDrop(bool on)
  294. {
  295.     SetFlag(eTextViewDragAndDrop, on);
  296. }
  297.  
  298. Rectangle TextView::BoundingRect(int from, int to)
  299. {
  300.     Point sp, ep;
  301.     int sl, el;
  302.     
  303.     if(from == start.ch) {
  304.     sp= start.viewp;
  305.     sl= start.line;
  306.     } else 
  307.     CharToPoint(from, &sl, &sp);
  308.     if (to == end.ch) {
  309.     ep= end.viewp;
  310.     el= end.line;
  311.     } else
  312.     CharToPoint(to, &el, &ep);
  313.  
  314.     Point rp(ep.x+1, ep.y + LineHeight(el));
  315.     Rectangle r= NormRect(sp, rp);
  316.     r.origin+= GetInnerOrigin();
  317.     return r;
  318. }
  319.  
  320. void TextView::RevealSelection()
  321. {
  322.     if (AnySelection()) {
  323.     Rectangle r= SelectionRect().Expand(Point(8,0));
  324.     RevealRect(r, r.extent);
  325.     }
  326. }
  327.      
  328. Rectangle TextView::SelectionRect()
  329. {
  330.     if (AnySelection()) 
  331.     return BoundingRect(start.ch, end.ch);
  332.     return gRect0;
  333. }
  334.  
  335. Command *TextView::DoLeftButtonDownCommand(Point lp, Token t, int clicks)
  336. {
  337.     SelPoint oldStart, oldEnd;
  338.     TextRangeFP rf= CharacterRange;
  339.     
  340.     oldStart= start;
  341.     oldEnd= end;
  342.  
  343.     if (! Enabled())
  344.     return View::DoLeftButtonDownCommand(lp, t, clicks);
  345.     
  346.     if (clicks >= 3)
  347.     rf= ParagraphRange;
  348.     else if (clicks >= 2) 
  349.     rf= WordRange;
  350.  
  351.     if (t.Flags == eFlgShiftKey) 
  352.     return new ExtendRangeSelector(this, rf);  
  353.     
  354.     if (clicks == 1) {
  355.     if (TestFlag(eTextViewDragAndDrop)) {
  356.         SelPoint n;
  357.         PointToPoint(lp-GetInnerOrigin(), &n.viewp, &n.line, &n.ch);
  358.         if (n.ch >= oldStart.ch && n.ch < oldEnd.ch)
  359.         return new DragAndDropSelector(this, oldStart.ch, oldEnd.ch, t.Flags == eFlgCntlKey);
  360.     }     
  361.     if (t.Flags == eFlgCntlKey) 
  362.         return new DragAndDropSelector(this, oldStart.ch, oldEnd.ch, TRUE); 
  363.  
  364.     if (t.Flags == (eFlgShiftKey|eFlgCntlKey))
  365.         return new QuickPasteSelector(this, oldStart.ch, oldEnd.ch);
  366.     }
  367.     if (t.Flags == 0)
  368.     return new RangeSelector(this, rf);
  369.     return View::DoLeftButtonDownCommand(lp, t, clicks);
  370. }
  371.  
  372. void TextView::SetStopChars(char *stops)
  373. {
  374.     strreplace(&stopChars, stops);
  375. }
  376.  
  377. Command *TextView::DoKeyCommand(int ch, Token)
  378. {
  379.     int nDelete= 0;
  380.     Token t;
  381.     bool newCmd, delSelection;       // delete the selection only
  382.  
  383.     if (! Writeable())
  384.     return gNoChanges;
  385.  
  386.     GrSetCursor(eCrsNone);
  387.     
  388.     if (ch != '\b' && Iscntrl(ch))
  389.     ch= inputmap[ch];
  390.     
  391.     newCmd= (typing == 0); // start new typing sequence?
  392.  
  393.     if (newCmd)
  394.     typing= MakeTypingCommand(cTYPEING, "Typing");
  395.  
  396.     delSelection= (newCmd && !Caret());
  397.     if (ch != '\b' || delSelection) {     
  398.     for (;;) {
  399.         if (ch != '\b' && ch != '\0') {
  400.         typing->AddChar();
  401.         scratchText->Append(ch);
  402.         }
  403.         if (scratchText->Size() == cMaxBatchedIns)
  404.         break;
  405.         gWindow->ReadEvent(t, 0);
  406.         if (t.IsAscii() && (byte)t.Code != '\b' 
  407.         // do not batch stopChars
  408.         && !(stopChars && strchr(stopChars, (byte)t.Code)) 
  409.         && ! TestFlag(eTextViewNoBatch)) {
  410.         ch= (byte) t.Code;
  411.         if (ch != '\b' && Iscntrl(ch)) 
  412.             ch= inputmap[ch];
  413.         } else {
  414.         if (t.Code != eEvtNone)
  415.             gWindow->PushBackEvent(t);
  416.         break;
  417.         } 
  418.     }
  419.     Paste(scratchText);
  420.     scratchText->Empty();
  421.     } else {
  422.     for (;;) {
  423.         typing->DelChar();
  424.         nDelete++;
  425.         gWindow->ReadEvent(t, 0);
  426.         if ((byte) t.Code != '\b') {
  427.         gWindow->PushBackEvent(t);
  428.         break;
  429.         }
  430.     }
  431.     DelChar(nDelete);
  432.     }
  433.     RevealSelection();
  434.     if (newCmd)
  435.     return typing;
  436.     return gNoChanges;
  437. }
  438.  
  439. Command *TextView::DoCursorKeyCommand(EvtCursorDir cd, Token)
  440. {
  441.     Point p;
  442.     int charNo= 0;
  443.  
  444.     if (! Enabled())
  445.     return gNoChanges;
  446.     switch (cd) {
  447.     case eCrsLeft:
  448.     charNo= start.ch-1;
  449.     break;
  450.     case eCrsRight:
  451.     charNo= end.ch+1;
  452.     break;
  453.     case eCrsUp:
  454.     charNo= CursorPos(start.ch, start.line, cd, p);
  455.     break;
  456.     case eCrsDown:
  457.     charNo= CursorPos(end.ch, end.line, cd, p);
  458.     break;
  459.     }
  460.     PrivSetSelection(charNo, charNo);
  461.     DoneTyping();
  462.     RevealSelection();
  463.     return gNoChanges;
  464. }
  465.  
  466. int TextView::CursorPos(int at, int line, EvtCursorDir d, Point p)
  467. {
  468.     int charNo;
  469.     Point basePoint, screenPoint;
  470.  
  471.     CharToPoint(at, &line, &screenPoint);
  472.     if (d == eCrsDown)
  473.     line= Math::Min(nLines-1, line+1);
  474.     else
  475.     line= Math::Max(0, line-1);        
  476.     basePoint= LineToPoint(line, TRUE) + Point(screenPoint.x, 0);
  477.     PointToPoint(basePoint, &p, &line, &charNo);
  478.     return charNo;
  479. }
  480.  
  481. GrCursor TextView::GetCursor(Point lp)
  482. {
  483.     if (TestFlag(eTextViewDragAndDrop)) {
  484.     SelPoint n;
  485.     PointToPoint(lp-GetInnerOrigin(), &n.viewp, &n.line, &n.ch);
  486.     if (n.ch >= start.ch && n.ch < end.ch) 
  487.         return eCrsBoldArrow;
  488.     }
  489.     return eCrsIBeam;
  490. }
  491.  
  492. void TextView::DoneTyping()
  493. {
  494.     typing= 0;
  495.     text->ResetCurrentCharStyle();
  496. }
  497.  
  498. void TextView::TypingDeleted(TypingCommand *t)
  499. {
  500.     if (t == typing)
  501.     typing= 0;
  502. }
  503.  
  504. TypingCommand *TextView::MakeTypingCommand(int cmdNo, char *name)
  505. {
  506.     return new TypingCommand(this, cmdNo, name);
  507. }
  508.  
  509. bool TextView::DeleteRequest(int,int)
  510. {
  511.     return TRUE;
  512. }
  513.  
  514. void TextView::Cut()
  515. {
  516.     if (!Writeable())
  517.     return; 
  518.     if (DeleteRequest(start.ch, end.ch) && AnySelection()) {
  519.     updateSelection= FALSE;
  520.     text->Cut(start.ch, end.ch);
  521.     updateSelection= TRUE;
  522.     PrivSetSelection(start.ch, start.ch, FALSE);
  523.     }
  524. }
  525.  
  526. void TextView::Copy(Text *t)
  527. {
  528.     text->Copy(t, start.ch, end.ch);
  529. }
  530.  
  531. void TextView::Paste(Text *t)
  532.     if (!Writeable())
  533.     return;
  534.     if ((!Caret() && !DeleteRequest(start.ch, end.ch)) || !AnySelection())
  535.     return;
  536.     updateSelection= FALSE;
  537.     text->Paste(t, start.ch, end.ch);
  538.     updateSelection= TRUE;
  539.     PrivSetSelection(start.ch+t->End(), start.ch+t->End(), FALSE);       
  540. }
  541.  
  542. Command *TextView::InsertText(Text *t)
  543. {
  544.     if (!Writeable())
  545.     return gNoChanges;
  546.  
  547.     bool newCmd= (typing == 0); // start new typing sequence?
  548.  
  549.     if (newCmd)
  550.     typing= MakeTypingCommand(cTYPEING, "Typing");
  551.  
  552.     typing->AddChar(t->End());
  553.     Paste(t);
  554.     if (newCmd)
  555.     return typing;
  556.     return gNoChanges;
  557. }
  558.  
  559. Command *TextView::InsertString(byte *str, int len)
  560. {
  561.     Text *t= text->MakeScratchText(str, len);
  562.     Command *cmd= InsertText(t);
  563.     SafeDelete(t);
  564.     return cmd;
  565. }
  566.  
  567. void TextView::DelChar(int n)
  568. {
  569.     int newStart= Math::Max(0, start.ch-n);
  570.     int changedLine= CharToLine(newStart);
  571.  
  572.     if (newStart == start.ch)
  573.     return;
  574.     if (newStart == 0 && start.ch == 0)
  575.     return;
  576.     if (DeleteRequest(newStart, start.ch)) {
  577.     updateSelection= FALSE;   
  578.     text->Cut(newStart, start.ch);
  579.     updateSelection= TRUE;
  580.     PrivSetSelection(newStart, newStart, FALSE);       
  581.     }
  582. }
  583.  
  584. Text *TextView::SetText(Text *t, bool scroll)
  585. {
  586.     Text *old= text;
  587.     SetFlag(eTextViewModified);
  588.     ForceRedraw();
  589.     old->RemoveObserver(this);
  590.     PrivSetSelection(0, 0, FALSE); // reset old selection
  591.     text= t;    
  592.     SafeDelete(scratchText);
  593.     scratchText= text->MakeScratchText(0, cMaxBatchedIns);
  594.     text->AddObserver(this);
  595.     RepairAll();
  596.     if (scroll == cRevealTop)
  597.     Scroll(cPartScrollAbs, gPoint0, FALSE);
  598.     Send(GetId(), cPartReplacedText, 0);
  599.     PrivSetSelection(0, 0, FALSE); // set new selection
  600.     typing= 0;
  601.     return old;
  602. }       
  603.  
  604. void TextView::SetString(byte *str, int len)
  605. {
  606.     ForceRedraw();
  607.     PrivSetSelection(0, 0, FALSE);
  608.     text->ReplaceWithStr(str, len);
  609.     RepairAll();
  610.     Send(GetId(), cPartReplacedText, 0);
  611.     DoneTyping();
  612.     PrivSetSelection(0, 0, FALSE);
  613.     typing= 0;
  614. }
  615.  
  616. void TextView::SetReadOnly(bool m)
  617. {
  618.     SetFlag(eTextViewReadOnly, m);
  619. }
  620.  
  621. bool TextView::GetReadOnly()
  622.     return TestFlag(eTextViewReadOnly); 
  623. }
  624.  
  625. void TextView::DoObserve(int, int part, void *what, Object *op)
  626. {
  627.     int d= 0, d1= 0;
  628.     
  629.     if (op == text && part == cPartSenderDied) {
  630.     text= 0;
  631.     return;    
  632.     }
  633.     
  634.     if (op == text) {
  635.     TextChanges tc= *(TextChanges*)what;
  636.     SetFlag(eTextViewModified);
  637.     switch (part) {
  638.     case eTextChangedRange:
  639.         marks->RangeChanged(tc.from, tc.to - tc.from);
  640.         Repair(tc.from, tc.to, FALSE);
  641.         if (updateSelection && AnySelection())
  642.         PrivSetSelection(tc.from, tc.to, TRUE);
  643.         break;
  644.  
  645.     case eTextDeleted:
  646.     case eTextReplaced:
  647.         marks->Replace(tc.from, tc.to, tc.size);
  648.         Repair(tc.from, tc.from+tc.size, FALSE);
  649.         if (updateSelection && AnySelection()) {
  650.         int p= tc.from+tc.size;
  651.         PrivSetSelection(p, p, TRUE);
  652.         }
  653.         break;
  654.     }
  655.     if (tc.pagination == cPaginationChange) 
  656.         ForceRedraw();
  657.     Send(GetId(), cPartChangedText, this);    // notify observers of the textview   
  658.     }
  659. }
  660.  
  661. bool TextView::PrintOnWhenObserving(Object *from)
  662. {
  663.     return GetText() != from;
  664. }
  665.  
  666. IStream& TextView::ReadFrom(IStream &s)
  667. {
  668.     StaticTextView::ReadFrom(s);
  669.     enabled= TRUE;
  670.     GetText()->AddObserver(this);
  671.     scratchText= text->MakeScratchText(0, cMaxBatchedIns);
  672.     return s;
  673. }
  674.  
  675. void TextView::NormSelection()
  676. {
  677.     if (start.ch > end.ch) 
  678.     SwapSelPoints(start, end);
  679. }
  680.  
  681. //---- menu related commands -----------------------------------------------
  682.  
  683. bool TextView::HasSelection()
  684. {
  685.     return AnySelection() && !Caret();
  686. }
  687.  
  688. Command *TextView::PasteData(Data *data)
  689. {
  690.     Text *t;
  691.     if (t= (Text*) data->AsObject(GetText()->IsA()))
  692.     return new PasteCommand(this, t);
  693.     return gNoChanges;
  694. }
  695.  
  696. bool TextView::CanPaste(Data *data)
  697. {
  698.     return data->CanConvert(GetText()->IsA());
  699. }
  700.  
  701. Menu *TextView::MakeMenu(int menuId)
  702. {
  703.     int font, size= gSysFont->Size();
  704.     GrFont fid= (GrFont)gSysFont->Fid();
  705.     char *fontname;
  706.     Menu *m;
  707.     
  708.     switch (menuId) {
  709.     case cFONTMENU:
  710.     m= new Menu(cFONTMENU, "Fonts", FALSE, TRUE, 0, 1, new SortedObjList);
  711.     for (font= 0; fontname= gFontManager->IdToName((GrFont)font); font++)
  712.         m->AppendItem(fontname, cFIRSTFONT+font);
  713.     break;
  714.         
  715.     case cSTYLEMENU:
  716.     m= new Menu(cSTYLEMENU, "Styles", FALSE);
  717.     m->Append(new MenuButtonItem(cFIRSTFACE+eFacePlain, 
  718.         new TextItem("Plain", new_Font(fid, size, eFacePlain))));
  719.     m->Append(new MenuButtonItem(cFIRSTFACE+eFaceBold,
  720.         new TextItem("Bold", new_Font(fid, size, eFaceBold)), "B"));
  721.     m->Append(new MenuButtonItem(cFIRSTFACE+eFaceItalic,
  722.         new TextItem("Italic", new_Font(fid, size, eFaceItalic)), "I"));
  723.     m->Append(new MenuButtonItem(cFIRSTFACE+eFaceUnderline,
  724.         new TextItem("Underline", new_Font(fid, size, eFaceUnderline)), "U"));
  725.     m->Append(new MenuButtonItem(cFIRSTFACE+eFaceOutline,
  726.         new TextItem("Outline", new_Font(fid, size, eFaceOutline))));
  727.     m->Append(new MenuButtonItem(cFIRSTFACE+eFaceShadow,
  728.         new TextItem("Shadow", new_Font(fid, size, eFaceShadow))));
  729.     break;
  730.     
  731.     case cSIZEMENU:
  732.     m= new Menu(cSIZEMENU, "Sizes", FALSE);
  733.     for (int sz= 9; sz <= 24; sz++)
  734.         m->AppendItem(form("%d  Points", sz), cFIRSTSIZE+sz); 
  735.     break;
  736.     
  737.     case cFORMATMENU:
  738.     m= new Menu(cFORMATMENU, "Format", FALSE);
  739.     m->Append(new MenuButtonItem(cFIRSTADJUST,   "Align Left"));
  740.     m->Append(new MenuButtonItem(cFIRSTADJUST+1, "Align Right"));
  741.     m->Append(new MenuButtonItem(cFIRSTADJUST+2, "Align Center"));
  742.     m->Append(new MenuButtonItem(cFIRSTADJUST+3, "Justify"));
  743.     m->AppendItems("-", 0);
  744.     m->Append(new MenuButtonItem(cFIRSTSPACING+0, "Single Spacing"));
  745.     m->Append(new MenuButtonItem(cFIRSTSPACING+18, "1-1/2 Spacing"));
  746.     m->Append(new MenuButtonItem(cFIRSTSPACING+24, "Double Spacing"));
  747.     break;
  748.     
  749.     }
  750.     return m;
  751. }
  752.  
  753. void TextView::DoSetupMenu(Menu *m)
  754. {    
  755.     View::DoSetupMenu(m);
  756.     switch (m->GetId()) {
  757.     case cEDITMENU:
  758.     SetupEditMenu(m);
  759.     break;
  760.     case cSIZEMENU:
  761.     SetupSizeMenu(m);
  762.     break;
  763.     case cFONTMENU:
  764.     SetupFontMenu(m);
  765.     break;
  766.     case cFORMATMENU:
  767.     SetupFormatMenu(m);
  768.     break;
  769.     case cSTYLEMENU:
  770.     SetupCharStyleMenu(m);
  771.     break;
  772.     case cVIEWMENU:
  773.     SetupViewMenu(m);
  774.     break;
  775.     }
  776. }
  777.  
  778. void TextView::SetupEditMenu(Menu *m)
  779. {
  780.     if (!GetReadOnly() && !Caret()) 
  781.     m->EnableItems(cCUT, cIMPORT, 0);
  782.     if (AnySelection())
  783.     m->EnableItems(cSELECTALL, cFIND, 0);
  784. }
  785.  
  786. void TextView::SetupSizeMenu(Menu *m)
  787. {
  788.     Font *fp= GetCharStyle()->GetFont();
  789.     m->CheckItem(cFIRSTSIZE+fp->Size());
  790. }
  791.  
  792. void TextView::SetupFontMenu(Menu *m)
  793. {
  794.     Font *fp= GetCharStyle()->GetFont();
  795.     m->CheckItem(gFontManager->NameToId(fp->Name())+cFIRSTFONT);
  796. }
  797.  
  798. void TextView::SetupViewMenu(Menu *m)
  799. {
  800.     m->EnableItem(cSHOWINVIS);
  801.     m->CheckItem(cSHOWINVIS, GetShowInvis(), cItemStateManyOf);
  802. }
  803.  
  804. void TextView::SetupCharStyleMenu(Menu *m)
  805. {
  806.     GrFace face= GetCharStyle()->GetFont()->Face();
  807.     m->CheckItem(cFIRSTFACE+eFacePlain, face == eFacePlain);
  808.     m->CheckItem(cFIRSTFACE+eFaceBold, (face&eFaceBold) == eFaceBold, cItemStateManyOf);
  809.     m->CheckItem(cFIRSTFACE+eFaceItalic, (face&eFaceItalic) == eFaceItalic, cItemStateManyOf);
  810.     m->CheckItem(cFIRSTFACE+eFaceUnderline, (face&eFaceUnderline) == eFaceUnderline, cItemStateManyOf);
  811.     m->CheckItem(cFIRSTFACE+eFaceOutline, (face&eFaceOutline) == eFaceOutline, cItemStateManyOf);
  812.     m->CheckItem(cFIRSTFACE+eFaceShadow, (face&eFaceShadow) == eFaceShadow, cItemStateManyOf);        
  813. }
  814.       
  815. void TextView::SetupFormatMenu(Menu *m)
  816. {
  817.     int from, to;
  818.     GetSelection(&from, &to);
  819.     ParaStyle *ps= text->GetParaStyle(from);
  820.     m->CheckItem(ps->GetProperty(eTxtPAlign)+cFIRSTADJUST);
  821.     m->CheckItem(ps->GetProperty(eTxtPSpacing)+cFIRSTSPACING);
  822.  
  823. Command *SetParaProp(TextView *view, int cmd, TxtParaProp p, int value)
  824. {
  825.     ParaDesc pd;
  826.     pd.SetProperty(p, value);
  827.     return 
  828.     new ParaStyleCommand(
  829.         view,
  830.         cmd,
  831.         "Change Formatting",
  832.         p,
  833.         pd
  834.     );
  835. }
  836.  
  837. Command *TextView::DoMenuCommand(int cmd)
  838. {
  839.     if (cmd >= cFIRSTSIZE && cmd <= cLASTSIZE) 
  840.     return new CharStyleCommand(
  841.             this, 
  842.             cmd, 
  843.             "Change Size", 
  844.             eTxtPSize, 
  845.             CharStyleSpec(
  846.                 eFontDefault, 
  847.                 eFacePlain,
  848.                 (GrFace)(cmd-cFIRSTSIZE)
  849.             )
  850.     );
  851.  
  852.     if (cmd >= cFIRSTFONT && cmd <= cLASTFONT) 
  853.     return new CharStyleCommand(
  854.             this, 
  855.             cmd, 
  856.             "Change Font", 
  857.             eTxTPFont, 
  858.             CharStyleSpec(
  859.                 (GrFont)(cmd-cFIRSTFONT),
  860.                 eFacePlain,
  861.                 0
  862.             )
  863.     );
  864.     
  865.     if (cmd >= cFIRSTFACE && cmd <= cLASTFACE) {
  866.     return new CharStyleCommand(
  867.             this, 
  868.             cmd, 
  869.             "Change Face",
  870.             eTxTPFace, 
  871.             CharStyleSpec(
  872.                 eFontDefault,
  873.                 (GrFace)(cmd-cFIRSTFACE),
  874.                 0
  875.             )
  876.     );
  877.     }
  878.  
  879.     if (cmd >= cFIRSTSPACING && cmd <= cLASTSPACING) {
  880.     ParaDesc pd;
  881.     pd.SetProperty(eTxtPSpacing, cmd-cFIRSTSPACING);
  882.     return 
  883.         new ParaStyleCommand(
  884.         this,
  885.         cmd,
  886.         "Change Formatting",
  887.         eTxtPSpacing,
  888.         pd
  889.         );
  890.     }
  891.  
  892.     /*
  893.     if (cmd >= cFIRSTCOLOR && cmd <= cLASTCOLOR) 
  894.     return new CharStyleCommand(
  895.             this, 
  896.             cmd, 
  897.             "Change Color",
  898.             eTxtPInk, 
  899.             CharStyleSpec(
  900.                 eFontDefault,
  901.                 eFacePlain,
  902.                 0,
  903.                 palette[cmd-cFIRSTCOLOR]
  904.             )
  905.     );
  906.     */
  907.     
  908.     if (cmd >= cFIRSTADJUST && cmd <= cLASTADJUST) 
  909.     return SetParaProp(this, cmd, eTxtPAlign, cmd-cFIRSTADJUST); 
  910.     
  911.     switch (cmd) {
  912.     case cCUT:
  913.     case cCOPY:
  914.     gClipBoard->SelectionToClipboard(SelectionAsText(), FALSE);
  915.     return new CutCopyCommand(this, cmd);
  916.     
  917.     case cSELECTALL:
  918.     SelectAll();
  919.     break;
  920.     
  921.     case cSHOWINVIS:
  922.     ShowInvisibles(!GetShowInvis());
  923.     return gNoChanges;
  924.     
  925.     default:
  926.     return View::DoMenuCommand(cmd);
  927.     }
  928.     return gNoChanges;
  929. }
  930.  
  931. bool TextView::SelectRegExpr(RegularExp *rex, bool dir)
  932. {
  933.     int selStart, selEnd, pos, matched;
  934.  
  935.     GetSelection(&selStart, &selEnd);
  936.     if (dir == cSearchForward)
  937.     pos= GetText()->Search(rex, &matched, selEnd);
  938.     else
  939.     pos= GetText()->Search(rex, &matched, Math::Max(0, selStart-1), cMaxInt, FALSE);
  940.  
  941.     if (pos != -1) {
  942.     PrivSetSelection(pos, pos+matched);
  943.     DoneTyping();
  944.     return TRUE;
  945.     }
  946.     return FALSE;
  947. }
  948.  
  949. void TextView::Home()
  950. {
  951.     PrivSetSelection(0, 0);
  952.     DoneTyping();
  953. }
  954.  
  955. void TextView::Bottom()
  956. {
  957.     PrivSetSelection(cMaxInt, cMaxInt);
  958.     DoneTyping();
  959. }
  960.  
  961. void TextView::SelectAll(bool redraw)
  962. {
  963.     PrivSetSelection(0, text->End(), redraw);
  964.     DoneTyping();
  965. }
  966.  
  967. CharStyle *TextView::GetCharStyle()
  968. {
  969.     int from, to;
  970.     GetSelection(&from, &to);
  971.     if (text->GetCurrentCharStyle())
  972.     return text->GetCurrentCharStyle();
  973.     else if (text->IsParaStart(from))
  974.     from++;
  975.     if (from == to)
  976.     from--;
  977.     return text->GetCharStyle(Math::Max(0, from));
  978. }
  979.  
  980.